package it.antonio.ilpizze.ioc;

import java.util.ArrayList;
import java.util.List;

import it.antonio.ilpizze.ioc.util.Injector;
import it.antonio.ilpizze.ioc.util.Matcher;
import it.antonio.ilpizze.reflection.Clazz;
import it.antonio.ilpizze.reflection.FieldAccessor;
import it.antonio.ilpizze.reflection.MethodAccessor;
import it.antonio.ilpizze.reflection.PropertyAccessor;

public abstract class DecorationFactory<T> {
	public abstract Decoration<T> decoration(Object key, Class<? extends T> type);
	
	public static <T> DecorationFactory<T> fromDecoration(final Decoration<T> decoration) {
		return new DecorationFactory<T>() {

			@Override
			public Decoration<T> decoration(Object key, Class<? extends T> type) {
				return decoration;
			}
			
		};
	}
	
	public static <T> DecorationFactory<T> initMethod(final Matcher<MethodAccessor> matcher){
		return new DecorationFactory<T>() {

			@Override
			public Decoration<T> decoration(Object key, Class<? extends T> type) {
				final List<MethodAccessor> initMethods = new ArrayList<MethodAccessor>();
				
				List<MethodAccessor> methods = Clazz.get(type).getMethods();
				for (MethodAccessor method : methods) {
					if (matcher.match(method)) {
						initMethods.add(method);
					}
				}
				return new Decoration<T>() {

					@Override
					public void decorate(T instance, Dependencies dependencies) {
						for(MethodAccessor initMethod: initMethods) initMethod.invoke(instance);
					}
				};
			}
		};
	}
	
	public static <T> DecorationFactory<T> fields(final Matcher<FieldAccessor> matcher, final Injector<FieldAccessor> injector){ 
		return new DecorationFactory<T>() {

			@Override
			public Decoration<T> decoration(Object key, Class<? extends T> type) {
				
				final List<FieldAccessor> fields = new ArrayList<FieldAccessor>();
				
				Clazz<?> clazz = Clazz.get(type);
				List<FieldAccessor> allFields = clazz.getFields();
				for (FieldAccessor field : allFields) {
					if (matcher.match(field)) {
						fields.add(field);
						
					}
				}
				return new Decoration<T>() {

					@Override
					public void decorate(T instance, Dependencies dependencies) {
						
						for(FieldAccessor field: fields) injector.inject(field, instance, dependencies);
						
					}
				};
			}
		};
	}
	public static <T> DecorationFactory<T> methods(final Matcher<MethodAccessor> matcher, final Injector<MethodAccessor> injector){ 
		return new DecorationFactory<T>() {

			@Override
			public Decoration<T> decoration(Object key, Class<? extends T> type) {
				
				final List<MethodAccessor> methods = new ArrayList<MethodAccessor>();
				
				Clazz<?> clazz = Clazz.get(type);
				List<MethodAccessor> allMethods = clazz.getMethods();
				for (MethodAccessor method : allMethods) {
					if (matcher.match(method)) {
						methods.add(method);
						
					}
				}
				return new Decoration<T>() {

					@Override
					public void decorate(T instance, Dependencies dependencies) {
						
						for(MethodAccessor method: methods) injector.inject(method, instance, dependencies);
						
					}
				};
			}
		};
	}
	public static <T> DecorationFactory<T> properties(final Matcher<PropertyAccessor> matcher, final Injector<PropertyAccessor> injector){ 
		return new DecorationFactory<T>() {

			@Override
			public Decoration<T> decoration(Object key, Class<? extends T> type) {
				
				final List<PropertyAccessor> selectedProperties = new ArrayList<PropertyAccessor>();
				
				Clazz<?> clazz = Clazz.get(type);

				List<PropertyAccessor> properties = clazz.findProperties();
				for (PropertyAccessor prop : properties) {
					if (matcher.match(prop)) {
						selectedProperties.add(prop);
					}
				}
				return new Decoration<T>() {

					@Override
					public void decorate(T instance, Dependencies dependencies) {
						
						for(PropertyAccessor prop: selectedProperties) injector.inject(prop, instance, dependencies);
						
					}
				};
			}
		};
	}
	
}
